unit u_auto;

interface

uses Graphics, ExtCtrls, Contnrs, u_Rechteck;


type
   T_Auto = class(TObject)

      procedure fahren(Sender:Tobject);
      procedure ablauf(Sender:Tobject);

   protected  // Variablen und Methoden in der erbenden Klasse sichtbar
              // private beschränkt den Zugriff auf die aktuelle Unit

      x_ol : integer;   //Koordinaten des Punktes
      y_ol : integer;   //    -- " --

      isVisible : boolean;

      Leinwand : TImage;  // Zeichenort des Punktes

      x_alt : integer;  //Speichert die vorherigen
      y_alt : integer;  //Positionen der Maus

      fx : integer;  //Variablen, die die Koordinaten bei
      fy : integer;  //zeitlicher Änderung beschreiben

      t_fahren : TTimer;  //Timer von Delphi, der die
                          //Zeiten für die Änderung der
                          //Koordinaten vorgibt;
      t_ablauf :TTimer;

      breite : integer;   //Breite des Autos
      hoehe : integer;    //Hoehe des Autos

      status : integer;  // speichert den aktuellen Status des Maennchens
                            // 0 : vor Start     1 : faehrt   2 : Rand oder Hindernis getroffen
                            // 5 : zeitende

      hindernisse : TObjectList; // Hierin werden die Hindernisse gespeichert

      zeit : integer; //gibt die Zeitin msec an

      AnzahlHindernisse : integer;   // Speichert die Anzahl der Hindernisse
      maxHindernisse : integer;      // Anzahl maximaler Hindernisse

      //Bestrafung bei Hindernis-Treffer festlegen
      treffer : integer;
      alt_treffer : integer;
      Strafe : boolean;
      MaxMove : integer;
      maxDefekt : integer;

      AutoStatus : TColor;    // noch ohne Funktion

      Punkte : integer;   // Speichert die erreichten Punkte
      Multiplikator : integer; // Multiplikator für Punktzaehlung

      Kilometer : integer;  //Speichert die zurückgelegten Kilometer des Autos



      procedure MachHindernis;

      function wandgetroffen:boolean;
      function CheckHindernis:boolean;
      procedure CheckEOL;

      
      procedure Stopp;


   public
      constructor Create(x_1, y_1, b, h:integer; ort: TImage);
      procedure Zeichne(art:integer);
      procedure Visible(modus:boolean);
      procedure Move(delta_x, delta_y : integer);
      procedure Lenken(maus_x,maus_y:integer);
      procedure NeuStart;
      procedure Start;

      procedure SetzStrafe(Status:boolean);
      procedure SetzZeit(neueZeit: integer);

      function GibPunkte : integer;
      function GibSpeed : integer;
      function GibGang : integer;
      function GibStatus : integer;
      function GibGroesse(typ : integer) : integer;
      function GibDefekt : integer;
      function GibStrecke : integer;
      function GibZeit : integer;
      function GibStoppGrund : integer;


   end;

implementation

{ T_Punkt }

// Prüft, ob ein Hindernis vom Auto berührt wurde
// und hält das Spiel bei einem Crash an
function T_Auto.CheckHindernis: boolean;
var
   i : integer;
   //treffer : integer;

begin
   //treffer := 0;  // Anzahl der Treffer ist Null


   // Die Liste mit den Hindernissen wird abgearbeitet
   // und auf eine Berührung geprüft
   // für jede Berührung wird die Trefferzahl hochgezählt
   for i := 0 to (hindernisse.Count-1) do
     if T_Rechteck(Hindernisse.Items[i]).InRechteck(x_ol,y_ol,breite, hoehe) = true then
        inc(treffer);

   // Bei mindestens einem Treffer ist das Spiel vorbei
   if treffer > maxDefekt then
   begin
      t_fahren.Enabled := false;
      t_ablauf.Enabled := false;
      result := true;

     { for i := 0 to (hindernisse.Count-1) do
         T_Rechteck(Hindernisse.Items[i]).Visible(false);}
   end
   else
   begin
      if treffer > alt_treffer then
      begin
         fx := 0;
         fy := 0;
         MaxMove := 1;
      end
      else
         MaxMove := 10;

      result := false;
   end;

   alt_treffer := treffer;

end;


// Constructor für das Auto
constructor T_Auto.Create(x_1, y_1, b, h:integer; ort: TImage);
begin
   Leinwand := ort;

   x_ol := x_1;
   y_ol := y_1;
   breite := b;
   hoehe := h;

   fx := 3;  //Default-Werte für Ortsänderungen
   fy := 3;

   // Timer für die Bewegung
   t_fahren := TTimer.Create(NIL);
   t_fahren.Enabled := false; //Timer abgeschaltet -> Auto bewegt sich nicht
   t_fahren.Interval := 80;   //Aktualisierungszeit für Timer in msec
   t_fahren.OnTimer := fahren; // Angabe der Prozedur/Methode die alle
                                // 80 msec aufgerufen wird


   // Timer für den Spielablauf; zählt die Zeit
   t_ablauf := TTimer.Create(NIL);
   t_ablauf.Enabled := false;
   t_ablauf.Interval := 50;
   t_ablauf.OnTimer := ablauf;
   zeit :=30000;


   isVisible := false;
   status := 0;
   Randomize;

   //Hindrnisliste wird initialisiert
   hindernisse := TObjectList.create(true);
   maxHindernisse := 50;

   MaxMove := 10;

   AutoStatus := clBlue;
   Punkte := 0;

   maxDefekt := 100;

   Kilometer := 0;
end;

// Fahren wird alle Interval-msec aufgerufen
// und bewegt das Objekt automatisch um
// fx und fx Einheiten
procedure T_Auto.fahren(Sender: Tobject);
begin
   Move(fx,fy);
end;

// Ablauf wird alle 50msec aufgerufen
procedure T_Auto.ablauf(Sender: Tobject);
begin
   Zeit := Zeit - 50;

   if Zeit = 0 then
   begin
      Stopp;
      status := 5;
      t_ablauf.enabled := false;
   end;

   Multiplikator := abs(fx * fy);
   Punkte := Punkte + hindernisse.Count * Multiplikator;

   checkEOL;

   MachHindernis;

end;

//Gibt die verbleibende Spielzeit zurücK
function T_Auto.GibZeit:integer;
begin
   if t_ablauf.Enabled = true then
   begin
      result := trunc(zeit/500);
   end
   else
      result := 0;

end;


// "Lenken" wertet die Mausbewegungen aus und übertragt
// sie auf das Grafikobjekt
procedure T_Auto.Lenken(maus_x, maus_y: integer);
begin
   // Die aktuellen Mauskoordinaten werden
   // mit den Koordinaten von "kurz vorher"
   // verglichen und daraus die Bewegung
   // ermittelt

   if status = 1 then  //lenken geht nur, wenn auto auxch faehrt
   begin
      if maus_x > x_alt then   //Maus wurde nach rechts bewegt
         if fx < MaxMove then inc(fx);

      if maus_x < x_alt then   //Maus wurde nach links bewegt
         if fx > -MaxMove then dec(fx);

      if maus_y > y_alt then   //Maus wurde nach unten bewegt
         if fy < MaxMove then inc(fy);

      if maus_y < y_alt then  //Maus wurde nach oben bewegt
         if fy > -MaxMove then dec(fy);

   end;

   x_alt := maus_x;  //Akuelle Mauskoordianten werden für
   y_alt := maus_y;  //nächsten Vergleich gespeichert
end;

procedure T_Auto.Move(delta_x, delta_y: integer);
begin
   if (wandgetroffen = false) AND (status < 2) AND (CheckHindernis = false) then
   begin
      Visible(false);   // Grafikobjekt wird unsichtbar auf der Zeichenfläche

      x_ol := x_ol + delta_x;   // Grafikobjekt bekommt neue Ursprungskoordinaten
      y_ol := y_ol + delta_y;   // zugewiesen


      Kilometer := Kilometer + trunc(Sqrt(sqr(delta_x) + sqr(delta_y)));

      Visible(true);   // Grafikobjekt wieder anzeigen
   end;


end;

// Timer einschalten -> Bewegung
procedure T_Auto.Start;
begin
   t_fahren.Enabled := true;
   t_ablauf.enabled := true;

   treffer := 0;
   alt_treffer := 0;

   status :=  1;
end;

// Timer ausschalten -> Stopp
procedure T_Auto.Stopp;
begin
   t_fahren.Enabled := false;
   t_ablauf.Enabled := false;

//   status :=  2;
end;

// Auto sichtbar machen
procedure T_Auto.Visible(modus: boolean);
begin
   if modus <> isVisible then
   begin
      Zeichne(0);
      isVisible := modus;
   end;
end;


// Prüft, ob eine begrenzende Wand getroffen wurde
function T_Auto.wandgetroffen:boolean;
var
   max_x : integer;
   max_y  : integer;

begin
   max_x := Leinwand.width;
   max_y := Leinwand.height;

   result := false;

   if ( x_ol <= 0) or ( y_ol <= 0)then
   begin
      stopp;
      status := 2;
      result := true;
   end;

   if ( x_ol + breite >= max_x) or (y_ol + hoehe >= max_y) then
   begin
      stopp;
      status := 2;
      result := true;
   end;


end;

// Auto zeichnen
procedure T_Auto.Zeichne(art:integer);
begin

   if art = 0 then
   begin
      Leinwand.Canvas.Brush.Color:= AutoStatus;
      Leinwand.Canvas.Rectangle(x_ol,y_ol,x_ol + breite,y_ol + hoehe);
      Leinwand.Canvas.FloodFill(x_ol, y_ol, clBlack, fsBorder);
      Leinwand.Canvas.Brush.Color:= clWhite;
   end;


end;


// Ein Hindernis anlegen, zeichnen und in der Hindernisliste speichern
procedure T_Auto.MachHindernis;
var
   neu_x : integer;
   neu_y : integer;

   neu_b : integer;
   neu_h : integer;

   zufall_x : integer;
   zufall_y : integer;

   neuHind : T_Rechteck;

begin

   if AnzahlHindernisse <= maxHindernisse then
   begin

   if (random(20) < 2) then
   begin
      inc(AnzahlHindernisse);

      zufall_x := random(leinwand.Width);
      zufall_y := random(leinwand.Height);

      neu_b := 5 + random(10);
      neu_h := 5 + random(10);
      {
      if x < 0 then
         zufall_x := zufall_x * (-1);

      if fy < 0 then
         zufall_y := zufall_y * (-1);}

      neu_x := zufall_x;
      neu_y := zufall_y;

      neuHind := T_Rechteck.Create(neu_x, neu_y, neu_b, neu_h,Leinwand);
      neuHind.Visible(true);
      hindernisse.Add(neuHind);

     // Leinwand.Canvas.Brush.Color:= clred;
     // Leinwand.Canvas.Rectangle(neu_x,neu_y, neu_x + neu_b, neu_y + neu_h);
     // Leinwand.Canvas.FloodFill(neu_x, neu_y, clBlack, fsBorder);
   end;
   end;
end;

procedure T_Auto.NeuStart;
begin
   fx := 3;
   fy := 3;

   x_ol := 10;
   y_ol := 10;

   hindernisse.Clear;
   zeit :=30000;

   Zeichne(0);

   status := 0;
   Punkte := 0;
   Kilometer := 0;
end;

procedure T_Auto.CheckEOL;
var
   ergebnis : boolean;
begin
   if (wandgetroffen = true) or (checkhindernis = true) then
   begin
      status := 2;
      stopp;
   end;

end;

procedure T_Auto.Setzstrafe(Status: boolean);
begin
   If Status = true then
      MaxDefekt := 0
   else
      MaxDefekt := 100;
end;

function T_Auto.GibPunkte: integer;
begin
   result := Punkte;
end;

function T_Auto.GibSpeed: integer;
begin
   result := (abs(fx) + abs(fy))*10;
end;

function T_Auto.GibGang: integer;
var
  tmp_speed, gang : integer;
begin
  //speed nochmal berechnen und abspeichern
  tmp_speed := (abs(fx) + abs(fy))*10;

  //man könnte alternativ ein hochschalten-Erlebnis
  //durch implementation von abfragen wie "> speed"
  //durchführen, die sich steigern
  //(scheint aber nicht gewünscht zu sein, da zu einfach)

  if tmp_speed = 0 then gang := 0
  else if (tmp_speed > 0) and (tmp_speed < 40) then gang := 1
  else if (tmp_speed > 39) and (tmp_speed < 80) then gang := 2
  else if (tmp_speed > 79) and (tmp_speed < 120) then gang := 3
  else if (tmp_speed > 119) and (tmp_speed < 160) then gang := 4
  else gang := 5; //da über 160
  result := gang;
end;

function T_Auto.GibStatus: integer;
begin
  result := status;
end;

function T_Auto.GibDefekt: integer;
begin
   result := treffer;
end;

function T_Auto.GibStrecke: integer;
begin
   result := Kilometer;
end;

function T_Auto.GibStoppGrund: integer;
begin
   result := Status;
end;

function T_Auto.GibGroesse(typ: integer): integer;
begin
if typ = 1 then
  result := x_ol
else if typ = 2 then
  result := y_ol
else if typ = 3 then
  result := breite
else if typ = 4 then
  result := hoehe;

  //x_ol,y_ol,breite, hoehe
end;

procedure T_Auto.SetzZeit(neueZeit: integer);
begin
  zeit := trunc(neueZeit * 500);
end;

end.
